//
//	MiscStringCompat.m
//		Written by Don Yacktman Copyright (c) 1993 by Don Yacktman.
//				Version 1.95  All rights reserved.
//		This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

#import <misckit/MiscString.h>

@implementation MiscString(Compat)

// This category includes methods to make the MiscString compatible
// with other existing string classes.  This way, you can easily
// replace your crufty old string class with a MiscString without
// having to change the code.  Most methods are covers which simply
// call the appropriate existing MiscString function.  The majority
// of these methods actually add compatability with the MOString class,
// but there is other stuff in here, too...

// None of the methods in this category add functionality; they are wrappers
// around existing methods which allow programmers to use calls associated
// with other string classes on the MiscString.  When I add compatability
// with string classes, if there is a function that adds functionality, I
// write a compatible method and place it into another category.  Only
// redundant methods are in this category, which is why they are not
// detailed in the documentation.

// If you have some _other_ string class, and would like to see
// compatibility methods added to the MiscString to support that
// class, too, let us know!

// Most of these methods are derived by Don Yacktman from code written
// by Mike Ferris and David Lehn.

- takeStringValue:sender { return [self takeStringValueFrom:sender]; }

- clear
{
	[self freeString];
	return self;
}

- ( const char* ) string
{
   return [self stringValue];
}

- setString: ( const char *) aString
{
	[self setStringValue:aString];
	return self;
}

- replaceChar: ( char ) aChar withChar: ( char ) replaceChar
{
	return [self replaceEveryInstanceOfChar:aChar withChar:replaceChar];
}

- addToEndOfString: ( const char *) aString
{
	[self cat:aString];
	return self;
}

- addToFrontOfString: ( const char *) aString
{
	[self insert:aString at:0];
	return self;
}

- addCharToEndOfString: ( char ) aChar
{
	[self addChar:aChar];
	return self;
}

- addCharToFrontOfString: ( char ) aChar
{
	[self insertChar:aChar at:0];
	return self;
}

- ( int ) numberFields
{
	return [self numWords];
}

- ( char * ) nthField: ( int ) fieldNumber useAsDelimiter: ( char ) c
{
	char * field;
	id fieldString = [ self extractPart:(fieldNumber) useAsDelimiter:c ];
	if (!fieldString) return NULL;
	field = NXZoneMalloc( [ self zone ], [ fieldString length ] + 1 );
	strcpy(field, [ fieldString stringValue ]);
	[fieldString free];
	return field;
}

- ( char * ) nthField: ( int ) fieldNumber
{	// have to do all this garbage to make sure we (1) don't leak memory
	// and (2) we don't smash the stringValueAndFree buffer.  (Otherwise
	// I would just use it, but that would make the MiscString badly
	// non-reentrant.)  This works, anyway...though the obvious
	// implementation would be this:
	//
	//		return [[self wordNum:fieldNumber] stringValueAndFree];

	static id tempBuffer = nil; id temp;

	if (!tempBuffer) tempBuffer = [MiscString new];
	temp = [self wordNum:fieldNumber];
	[tempBuffer setStringValue:[temp stringValue]];
	[temp free];
	return (char *)[tempBuffer stringValue];
}

// this one kind of adds functionality, but by naming conventions it still
// makes sense to put it here.
- ( char * ) nthQuotedField: ( int ) fieldNumber
{
	return [ self nthField:fieldNumber*2 useAsDelimiter:'"' ];
}

- ( char * ) lastField
{
	char * field;
	id str = [ self extractPart:MISC_STRING_LAST useAsDelimiter:' ' ];
	field = NXZoneMalloc([self zone], [str length] + 1);
	strcpy(field, [str stringValue]);
	[str free];
	return field;
}

- ( char * ) firstField
{
	char * field;
	id str = [ self extractPart:MISC_STRING_FIRST useAsDelimiter:' ' ];
	field = NXZoneMalloc([self zone], [str length] + 1);
	strcpy(field, [str stringValue]);
	[str free];
	return field;
}

- addStrings: ( const char *) fields,  ...
{
	va_list ptr;
  
	va_start( ptr, fields );
	[self catStrings:fields, ptr];
	va_end( ptr );       
	return self;
}

- replaceEveryInstanceOfChar:(char)aChar withChar:(char)replaceChar
{
	return [self replaceEveryOccurrenceOfChar:aChar
			withChar:replaceChar caseSensitive:YES];
}

- replaceEveryInstanceOfChar:(char)aChar with:(char)replaceChar
{
	return [self replaceEveryOccurrenceOfChar:aChar
			withChar:replaceChar caseSensitive:YES];
}

// The rest of the methods here are for compatability with the MOString
// from the MOKit.

// warning:  the following MOString methods clash with MiscString methods
//	of the same name.  You will have to check your code for uses of these
//	methods and fix them appropriately to avoid serious problems.
//	Group 1:  MOString wants string object, MiscString wants char *
//		-insert:stringObject at:(int)position
//		-cat:stringObject
//	Note also that the MiscString doesn't support the unique string
//	concept, so it cannot become a unique string.  Perhaps in the
//	future there will be a MiscUniqueString class; I prefer that approach
//	myself.  At any rate, this shouldn't affect the functionality when
// using the MiscString compatibly.

- convertToUpper
{
	return [self toUpper];
}

- convertToLower
{
	return [self toLower];
}

- (int)replaceAllOccurrencesOfChar:(char)oldChar with:(char)newChar
{
	[self replaceEveryOccurrenceOfChar:oldChar withChar:newChar];
	return 0; // this information isn't available so we'll just return zero
}

- (char)replaceCharAt:(int)index with:(char)newChar
{
	char tempChar = [self charAt:index];
	[self replaceCharAt:index withChar:newChar];
	return tempChar; 
}

- insertStringValue:(const char *)s at:(int)position
{
	[self insert:s at:position];
	return self;
}

- preCatStringValue:(const char *)s
{
	return [self insert:s at:0];
}

- preCat:stringObject
{
	return [self insertString:stringObject];
}

- preCatFromFormat:(const char *)format, ...
// Prepends the given format string after formatting before the contents
// of the receiver.
{
	va_list param_list;

	va_start(param_list, format);
	[self insertFromFormat:format, param_list];
	va_end(param_list);
	return self;
}

- catStringValue:(const char *)s
{
	return [self cat:s];
}

- (int)compare:stringObject caseSensitive:(BOOL)flag
{
	return [self compare:stringObject caseSensitive:flag 
					length:-1 withTable:NULL];
}

- (int)compare:stringObject caseSensitive:(BOOL)flag length:(int)len
{
	return [self compare:stringObject caseSensitive:flag 
					length:len withTable:NULL];
}

- (int)compare:stringObject caseSensitive:(BOOL)flag length:(int)len 
			withTable:(NXStringOrderTable *)table
{
	int ret;
	NXStringOrderTable *realTable = [self stringOrderTable]; // temp save
	[self setStringOrderTable:table];
	ret = [self compareTo:stringObject n:len caseSensitive:flag];
	[self setStringOrderTable:realTable];
	return ret;
}

- (int)compareStr:(const char *)s
{
	return [self compareStr:s caseSensitive:YES length:-1 withTable:NULL];
}

- (int)compareStr:(const char *)s caseSensitive:(BOOL)flag
{
	return [self compareStr:s caseSensitive:flag length:-1 withTable:NULL];
}

- (int)compareStr:(const char *)s caseSensitive:(BOOL)flag length:(int)len
{
	return [self compareStr:s caseSensitive:flag length:len withTable:NULL];
}

- (int)compareStr:(const char *)s caseSensitive:(BOOL)flag length:(int)len
			withTable:(NXStringOrderTable *)table
{
	id ss = [[self class] newWithString:s];
	int ret = [self compare:ss caseSensitive:flag length:len withTable:table];
	[ss free];
	return ret;
}

- (int)endCompare:stringObject caseSensitive:(BOOL)flag
{
	return [self endCompare:stringObject 
					caseSensitive:flag length:-1 withTable:NULL];
}

- (int)endCompare:stringObject caseSensitive:(BOOL)flag length:(int)len
{
	return [self endCompare:stringObject 
					caseSensitive:flag length:len withTable:NULL];
}

- (int)endCompare:stringObject caseSensitive:(BOOL)flag length:(int)len 
			withTable:(NXStringOrderTable *)table
{
	int ret;
	NXStringOrderTable *realTable = [self stringOrderTable]; // temp save
	[self setStringOrderTable:table];
	ret = [self endCompareTo:stringObject n:len caseSensitive:flag];
	[self setStringOrderTable:realTable];
	return ret;
}

- (int)endCompareStr:(const char *)s
{
	return [self endCompareStr:s caseSensitive:YES 
				length:-1 withTable:NULL];
}

- (int)endCompareStr:(const char *)s caseSensitive:(BOOL)flag
{
	return [self endCompareStr:s caseSensitive:flag 
				length:-1 withTable:NULL];
}

- (int)endCompareStr:(const char *)s caseSensitive:(BOOL)flag 
			length:(int)len
{
	return [self endCompareStr:s caseSensitive:flag 
				length:len withTable:NULL];
}

- (int)endCompareStr:(const char *)s caseSensitive:(BOOL)flag 
			length:(int)len withTable:(NXStringOrderTable *)table
{
	id ss = [[self class] newWithString:s];
	int ret = [self endCompare:ss caseSensitive:flag
			length:len withTable:table];
	[ss free];
	return ret;
}

- substringFrom:(int)start to:(int)end
{
	return [self midFrom:start to:end];
}

- (int)positionOf:(char)aChar nthOccurrence:(int)n
{
	if (n<0) return [self rspotOf:aChar occurrenceNum:(-n) caseSensitive:YES];
	return [self spotOf:aChar occurrenceNum:n caseSensitive:YES];
}

- (int)countOccurrencesOf:(char)aChar
{
	return [self numOfChar:aChar caseSensitive:YES];
}

- (size_t)strlen
{
	return length;
}

- (const char *)strcpy:(const char *)s
{
	[self setStringValue:s];
	return buffer;
}

- (const char *)strncpy:(const char *)s :(size_t)n
{
	[self setStringValue:s n:(int)n fromZone:[self zone]];
	return buffer;
}

- (const char *)strcat:(const char *)s
{
	[self cat:s];
	return buffer;
}

- (const char *)strncat:(const char *)s :(size_t)n
{
	[self cat:s n:n fromZone:[self zone]];
	return buffer;
}

- (int)strcmp:(const char *)s
{
	if ((!buffer) || (!s)) return -2;
	return strcmp(buffer, s);
}

- (int)strncmp:(const char *)s :(size_t)n
{
	if ((!buffer) || (!s)) return -2;
	return strncmp(buffer, s, n);
}

- (const char *)strchr:(char)aChar
{
	if (buffer) return NULL;
	return strchr(buffer, aChar);
}

- (const char *)strrchr:(char)aChar
{
	if (!buffer) return NULL;
	return strrchr(buffer, aChar);
}

- (const char *)strpbrk:(const char *)breakChars
{
	if ((!buffer) || (!breakChars)) return NULL;
	return strpbrk(buffer, breakChars);
}

- (size_t)strspn:(const char *)acceptableChars
{
	if ((!buffer) || (!acceptableChars)) return -1;
	return strspn(buffer, acceptableChars);
}

- (size_t)strcspn:(const char *)breakChars
{
	if ((!buffer) || (!breakChars)) return 0;
	return strcspn(buffer, breakChars);
}

- initStringValue:(const char *)s
{
	return [self initString:s];
}

- initStringValueNoCopy:(char *)s
{	// we still copy it anyway; this could be implemented for effect a speedup
	// if it makes that much difference...
	return [self initString:s];
}

- initStringValueNoCopy:(char *)s shouldFree:(BOOL)flag
{
	id ret = [self initString:s];
	if (flag) NX_FREE(s);
	return ret;
}

- initStringValueUnique:(const char *)s
{
	return [self initString:s];
}

- deepCopy
{
	return [self deepCopyFromZone:[self zone]];
}

- deepCopyFromZone:(NXZone *)zone
{ // our copy from zone IS deep already...
	return [self copyFromZone:zone];
}

- shallowCopy
{
	return [self shallowCopyFromZone:[self zone]];
}

- shallowCopyFromZone:(NXZone *)zone
{ // our copy from zone IS deep already...
	return [self copyFromZone:zone];
}

- setStringValueNoCopy:(const char *)s
{	// we set the string value by copying anyway.
	return [self setStringValue:s];
}

- setStringValueNoCopy:(char *)s shouldFree:(BOOL)flag
{
	id ret = [self setStringValue:s];
	if (flag) NX_FREE(s);
	return ret;
}

- setStringValueUnique:(const char *)s { return [self setStringValue:s]; }
- setNull { return [self freeString]; }
- makeUnique { return self; } // we don't support this at all.
- setShouldFree:(BOOL)flag { return self; }
- (unsigned int)count { return length; }
- (BOOL)isNull { return [self emptyString]; }
- (BOOL)isEmpty { return [self emptyString]; }
- (BOOL)isUnique { return NO; }
- (BOOL)shouldFree { return YES; }




// MOPathString methods
- (int)numberOfComponents { return [self numberOfPathComponents]; }
- componentAt:(int)index { return [self pathComponentAt:index]; }
- file { return [self fileName]; }
- directory { return [self pathName]; }
- (const char *)path { return [self stringValue]; }
- (BOOL)isRelative { return [self isRelativePath]; }
- (BOOL)isAbsolute { return [self isAbsolutePath]; }
- (BOOL)isDirectory { return [self isFileOfType:Misc_Directory]; }
- (BOOL)isPlainFile { return [self isFileOfType:Misc_PlainFile]; }
- (BOOL)isSymbolicLink { return [self isFileOfType:Misc_SymbolicLink]; }
- (BOOL)isCharacterSpecial { return [self isFileOfType:Misc_CharacterSpecial]; }
- (BOOL)isBlockSpecial { return [self isFileOfType:Misc_BlockSpecial]; }
- (BOOL)isSocket { return [self isFileOfType:Misc_Socket]; }
- setPathSeparator:(char)c { return self; } // NO OP
- setExtensionSeparator:(char)c { return self; } // NO OP
- (char)pathSeparator { return '/'; }
- (char)extensionSeparator { return '.'; }
- setPath:(const char *)path { return [self setStringValue:path]; }
- initPath:(const char *)path { return [self initStringValue:path]; }
- (char *)buffer { return buffer; } // be careful with this!  don't muck it up!


// NSString emulations.  First two courtesy of Scott Anguish:
- (BOOL)hasPrefix:(const char *)value
{
	return ([self cmp:value n:strlen(value)]==0);
}

- (BOOL)hasPrefixString:value
{
	return ([self compareTo:value n:[value length]]==0);
}

@end
